package com.zhentao.studyim.service;

import com.alibaba.fastjson2.JSON;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis服务类
 *
 * 提供完整的Redis缓存和状态管理功能：
 * - 用户在线状态管理
 * - 用户会话信息存储
 * - 消息缓存管理
 * - 系统统计信息
 * - 消息发送频率限制
 * - 容错机制和降级处理
 *
 * 数据结构设计：
 * - user:online:{userId} -> Boolean (在线状态)
 * - user:session:{userId} -> Hash (会话信息)
 * - chat:recent:{chatId} -> List (最近消息)
 * - stats:* -> String/Number (统计数据)
 * - rate:limit:* -> Number (限流计数)
 *
 * @author zhentao
 * @version 1.0
 * @since 2025-01-22
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RedisService {

    private final RedisTemplate<String, Object> redisTemplate;
    private final InMemoryUserStatusService inMemoryUserStatusService;

    // Redis键前缀常量
    private static final String USER_ONLINE_PREFIX = "user:online:";
    private static final String USER_SESSION_PREFIX = "user:session:";
    private static final String CHAT_RECENT_PREFIX = "chat:recent:";
    private static final String STATS_PREFIX = "stats:";
    private static final String RATE_LIMIT_PREFIX = "rate:limit:";

    // ==================== 用户在线状态管理 ====================

    /**
     * 设置用户在线状态
     */
    public void setUserOnline(Long userId) {
        try {
            String key = USER_ONLINE_PREFIX + userId;
            redisTemplate.opsForValue().set(key, true, 24, TimeUnit.HOURS);
            
            // 更新在线用户统计
            updateOnlineUserCount();
            
            log.debug("用户 {} 设置为在线状态", userId);
        } catch (Exception e) {
            log.warn("Redis设置用户在线状态失败，使用内存存储: {}", e.getMessage());
            inMemoryUserStatusService.setUserOnline(userId);
        }
    }

    /**
     * 设置用户离线状态
     */
    public void setUserOffline(Long userId) {
        try {
            String key = USER_ONLINE_PREFIX + userId;
            redisTemplate.delete(key);
            
            // 清理用户会话
            clearUserSession(userId);
            
            // 更新在线用户统计
            updateOnlineUserCount();
            
            log.debug("用户 {} 设置为离线状态", userId);
        } catch (Exception e) {
            log.warn("Redis设置用户离线状态失败，使用内存存储: {}", e.getMessage());
            inMemoryUserStatusService.setUserOffline(userId);
        }
    }

    /**
     * 检查用户是否在线
     */
    public boolean isUserOnline(Long userId) {
        try {
            String key = USER_ONLINE_PREFIX + userId;
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            log.warn("Redis检查用户在线状态失败，使用内存存储: {}", e.getMessage());
            return inMemoryUserStatusService.isUserOnline(userId);
        }
    }

    /**
     * 获取所有在线用户ID列表
     */
    public List<Long> getOnlineUserIds() {
        try {
            Set<String> keys = redisTemplate.keys(USER_ONLINE_PREFIX + "*");
            List<Long> userIds = new ArrayList<>();
            
            if (keys != null) {
                for (String key : keys) {
                    String userIdStr = key.replace(USER_ONLINE_PREFIX, "");
                    try {
                        userIds.add(Long.valueOf(userIdStr));
                    } catch (NumberFormatException e) {
                        log.warn("无效的用户ID格式: {}", userIdStr);
                    }
                }
            }
            
            return userIds;
        } catch (Exception e) {
            log.error("获取在线用户列表失败: {}", e.getMessage());
            return new ArrayList<>();
        }
    }

    // ==================== 用户会话管理 ====================

    /**
     * 存储用户会话信息
     */
    public void setUserSession(Long userId, Map<String, Object> sessionData) {
        try {
            String key = USER_SESSION_PREFIX + userId;
            redisTemplate.opsForHash().putAll(key, sessionData);
            redisTemplate.expire(key, 24, TimeUnit.HOURS);
            
            log.debug("用户 {} 会话信息已存储", userId);
        } catch (Exception e) {
            log.error("存储用户会话信息失败: {}", e.getMessage());
        }
    }

    /**
     * 获取用户会话信息
     */
    public Map<Object, Object> getUserSession(Long userId) {
        try {
            String key = USER_SESSION_PREFIX + userId;
            return redisTemplate.opsForHash().entries(key);
        } catch (Exception e) {
            log.error("获取用户会话信息失败: {}", e.getMessage());
            return new HashMap<>();
        }
    }

    /**
     * 清理用户会话
     */
    public void clearUserSession(Long userId) {
        try {
            String key = USER_SESSION_PREFIX + userId;
            redisTemplate.delete(key);
            
            log.debug("用户 {} 会话信息已清理", userId);
        } catch (Exception e) {
            log.error("清理用户会话信息失败: {}", e.getMessage());
        }
    }

    // ==================== 消息缓存管理 ====================

    /**
     * 缓存最近的聊天消息
     */
    public void cacheRecentMessage(Long fromUserId, Long toUserId, String messageContent) {
        try {
            // 生成聊天室ID（较小的用户ID在前）
            String chatId = generateChatId(fromUserId, toUserId);
            String key = CHAT_RECENT_PREFIX + chatId;
            
            // 构造消息对象
            Map<String, Object> message = new HashMap<>();
            message.put("fromUserId", fromUserId);
            message.put("toUserId", toUserId);
            message.put("content", messageContent);
            message.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
            
            // 添加到列表头部，保留最近50条消息
            redisTemplate.opsForList().leftPush(key, JSON.toJSONString(message));
            redisTemplate.opsForList().trim(key, 0, 49);
            
            // 设置过期时间为7天
            redisTemplate.expire(key, 7, TimeUnit.DAYS);
            
            log.debug("消息已缓存到聊天室: {}", chatId);
        } catch (Exception e) {
            log.error("缓存消息失败: {}", e.getMessage());
        }
    }

    /**
     * 获取缓存的最近消息
     */
    public List<Map<String, Object>> getRecentMessages(Long userId1, Long userId2, int limit) {
        try {
            String chatId = generateChatId(userId1, userId2);
            String key = CHAT_RECENT_PREFIX + chatId;
            
            List<Object> messages = redisTemplate.opsForList().range(key, 0, limit - 1);
            List<Map<String, Object>> result = new ArrayList<>();
            
            if (messages != null) {
                for (Object msg : messages) {
                    try {
                        @SuppressWarnings("unchecked")
                        Map<String, Object> messageMap = JSON.parseObject(msg.toString(), Map.class);
                        result.add(messageMap);
                    } catch (Exception e) {
                        log.warn("解析缓存消息失败: {}", e.getMessage());
                    }
                }
            }
            
            return result;
        } catch (Exception e) {
            log.error("获取缓存消息失败: {}", e.getMessage());
            return new ArrayList<>();
        }
    }

    // ==================== 统计信息管理 ====================

    /**
     * 更新在线用户数量统计
     */
    public void updateOnlineUserCount() {
        try {
            List<Long> onlineUsers = getOnlineUserIds();
            String key = STATS_PREFIX + "online:count";
            redisTemplate.opsForValue().set(key, onlineUsers.size(), 1, TimeUnit.HOURS);
            
            log.debug("在线用户数量已更新: {}", onlineUsers.size());
        } catch (Exception e) {
            log.error("更新在线用户数量失败: {}", e.getMessage());
        }
    }

    /**
     * 获取在线用户数量
     */
    public int getOnlineUserCount() {
        try {
            String key = STATS_PREFIX + "online:count";
            Object count = redisTemplate.opsForValue().get(key);
            return count != null ? Integer.parseInt(count.toString()) : 0;
        } catch (Exception e) {
            log.error("获取在线用户数量失败: {}", e.getMessage());
            return 0;
        }
    }

    /**
     * 增加消息发送统计
     */
    public void incrementMessageCount() {
        try {
            String today = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            String key = STATS_PREFIX + "message:count:" + today;
            redisTemplate.opsForValue().increment(key);
            redisTemplate.expire(key, 30, TimeUnit.DAYS);
            
            log.debug("今日消息数量已增加");
        } catch (Exception e) {
            log.error("增加消息统计失败: {}", e.getMessage());
        }
    }

    /**
     * 获取今日消息数量
     */
    public long getTodayMessageCount() {
        try {
            String today = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            String key = STATS_PREFIX + "message:count:" + today;
            Object count = redisTemplate.opsForValue().get(key);
            return count != null ? Long.parseLong(count.toString()) : 0;
        } catch (Exception e) {
            log.error("获取今日消息数量失败: {}", e.getMessage());
            return 0;
        }
    }

    // ==================== 限流控制 ====================

    /**
     * 检查用户发送消息频率限制
     */
    public boolean checkMessageRateLimit(Long userId) {
        try {
            // 先测试Redis连接
            if (!testRedisConnection()) {
                log.warn("Redis连接失败，跳过频率限制检查");
                return true;
            }

            String key = RATE_LIMIT_PREFIX + "message:" + userId;
            String currentMinute = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm"));
            String fullKey = key + ":" + currentMinute;

            Long count = redisTemplate.opsForValue().increment(fullKey);
            redisTemplate.expire(fullKey, 1, TimeUnit.MINUTES);

            // 每分钟最多发送30条消息
            boolean allowed = count != null && count <= 30;

            if (!allowed) {
                log.warn("用户 {} 发送消息过于频繁，已被限制", userId);
            }

            return allowed;
        } catch (Exception e) {
            log.error("检查消息频率限制失败: {}", e.getMessage());
            return true; // 出错时允许发送
        }
    }

    /**
     * 测试Redis连接
     */
    public boolean testRedisConnection() {
        try {
            redisTemplate.opsForValue().set("test:connection", "ok", 10, TimeUnit.SECONDS);
            Object result = redisTemplate.opsForValue().get("test:connection");
            boolean connected = "ok".equals(String.valueOf(result));
            if (connected) {
                log.debug("Redis连接测试成功");
            } else {
                log.error("Redis连接测试失败：返回值不匹配，实际值: {}", result);
            }
            return connected;
        } catch (Exception e) {
            log.error("Redis连接测试失败: {}", e.getMessage());
            return false;
        }
    }

    // ==================== 工具方法 ====================

    /**
     * 生成聊天室ID
     */
    private String generateChatId(Long userId1, Long userId2) {
        return userId1 < userId2 ? userId1 + "_" + userId2 : userId2 + "_" + userId1;
    }

    /**
     * 清理过期数据
     */
    public void cleanExpiredData() {
        try {
            // 这里可以添加定期清理逻辑
            log.info("Redis过期数据清理完成");
        } catch (Exception e) {
            log.error("清理过期数据失败: {}", e.getMessage());
        }
    }
}
